package cn.jtfadmin.base.lang.common.utils;

import cn.jtfadmin.base.lang.common.domain.entity.IdEntity;
import cn.jtfadmin.base.lang.common.service.BaseService;
import cn.jtfadmin.base.lang.config.ApplicationHolder;
import lombok.Data;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.*;

/**
 * ak 工具
 */
public class AkUtils {

    /**
     * 合并字段名称<br/>
     * 将第二个参数的首字母大写，后缀到第一个参数<br/>
     * 如果是集合将在后面增加s标示复数形式<br/>
     * 使用位置，代码生成器生成的代码如果是集合或者对象生成的model需要合并字段名称<br/>
     * @see #copyProperties(Object, Object, String...)
     * @param name1          第一个字段值
     * @param name2          第二哥字段值
     * @param collectionFlag 是否是集合
     * @return 合并字段名称
     */
    public static String megerFieldName(String name1, String name2, boolean collectionFlag) {
        String name2First = name2.substring(0, 1);
        String name2Last = name2.substring(1);
        StringBuilder stringBuilder = new StringBuilder(name1.length()
                + name2.length() + (collectionFlag ? 1 : 0));
        stringBuilder.append(name1)
                .append(name2First.toUpperCase())
                .append(name2Last);
        if (collectionFlag) {
            stringBuilder.append("s");
        }
        return stringBuilder.toString();
    }

    /**
     * 获取合并字段可能的源名称
     *
     * @param megerFieldNameStr
     * @return
     */
    public static List<MegerFieldName> findMegerFieldSourceName(String megerFieldNameStr) {
        List<MegerFieldName> result = new ArrayList<>();
        int index = megerFieldNameStr.length() - 1;
        for (; index > 0; index--) {
            if (Character.isUpperCase(megerFieldNameStr.charAt(index))) {
                MegerFieldName megerFieldName1 = new MegerFieldName();
                megerFieldName1.setSourceName(megerFieldNameStr.substring(0, index));
                megerFieldName1.setMegerName(megerFieldNameStr.substring(index, index + 1).toLowerCase() + megerFieldNameStr.substring(index + 1));
                result.add(megerFieldName1);
                if (megerFieldName1.getMegerName().endsWith("s")) {
                    megerFieldName1 = new MegerFieldName();
                    megerFieldName1.setSourceName(megerFieldNameStr.substring(0, index));
                    megerFieldName1.setMegerName(megerFieldNameStr.substring(index, index + 1).toLowerCase() + megerFieldNameStr.substring(index + 1, megerFieldNameStr.length() - 1));
                    result.add(megerFieldName1);
                }
            }
        }
        if (index == 0 || index == megerFieldNameStr.length() - 1) {
            return result;
        }

        return result;
    }

    @Data
    public static class MegerFieldName {
        private String sourceName;

        private String megerName;

    }

    /**
     * 合并字段备注<br/>
     * 如果为空返回不合并<br/>
     * 使用位置，代码生成器生成的代码如果是集合或者对象生成的model需要合并字段备注<br/>
     *
     * @param comment1 备注1
     * @param comment2 备注2
     * @return 合并字段备注
     */
    public static String megerFieldCommont(String comment1, String comment2) {
        return getNotNullString(comment1, "")
                + getNotNullString(comment2, "");
    }


    /**
     * 获取第一个非空字符串
     *
     * @param strings 字符串集合
     * @return 第一个非空字符串，如果没有返回空串
     */
    public static String getNotNullString(String... strings) {
        if (Objects.isNull(strings)) {
            return "";
        }
        for (String string : strings) {

            if (StringUtils.hasText(string)) {
                return string;
            }
        }
        return "";
    }

    public static <T> T copyProperties(Object source, Class<T> targetClass, String... ignoreFields){
        T t = null;
        try {
            t = targetClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        AkUtils.copyProperties(source, t, ignoreFields);
        return t;
    }


    public static void copyProperties(Object source, Object target, String... ignoreFields) {
        BeanUtils.copyProperties(source, target, ignoreFields);
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        Class<?> actualEditable = target.getClass();

        PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor targetPd : targetPds) {
            Method writeMethod = targetPd.getWriteMethod();
            if(Objects.isNull(writeMethod)){
                // 此属性没有写方法
                continue;
            }
            PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
            if (sourcePd != null) {
                // // 正常的已通过spring 复制过了
                continue;
            }
            // 写方法的参数类型
            Class<?> parameterType = writeMethod.getParameterTypes()[0];
            // 参数是字符串， 可能是嵌套对象属性/嵌套集合对象属性

            if (Collection.class.isAssignableFrom(parameterType)) {
                // 字符串转集合
                PropertyDescriptor readDescirptor = BeanUtils.getPropertyDescriptor(source.getClass(), megerFieldName(targetPd.getName(),
                        "id", true));
                if (readDescirptor != null && readDescirptor.getReadMethod() != null
                        && String.class.isAssignableFrom(readDescirptor.getReadMethod().getReturnType())) {
                    Method readMethod = readDescirptor.getReadMethod();
                    if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                        readMethod.setAccessible(true);
                    }
                    if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                        writeMethod.setAccessible(true);
                    }
                    try {
                        String value = String.class.cast(readMethod.invoke(source));
                        if (StringUtils.hasText(value)) {
                            ResolvableType resolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
                            ResolvableType generic = resolvableType.getGeneric(0);
                            Class<?> resolve = generic.resolve();
                            Collection result = null;
                            if (List.class.isAssignableFrom(parameterType)) {
                                result = new ArrayList<>();
                            } else if (Set.class.isAssignableFrom(parameterType)) {
                                result = new HashSet<>();
                            } else {
                                // 不支持的类型
                                continue;
                            }
                            if (!IdEntity.class.isAssignableFrom(resolve)) {
                                continue;
                            }
                            ApplicationContext applicationContext = ApplicationHolder.getApplicationContext();
                            String[] beanNamesForType = applicationContext.getBeanNamesForType(
                                    ResolvableType.forClassWithGenerics(BaseService.class, resolve)
                            );
                            if(beanNamesForType.length>0){
                                BaseService bean = applicationContext.getBean(beanNamesForType[0], BaseService.class);
                                result.addAll(bean.findByIds(value));
                            }
                            writeMethod.invoke(target, result);
                        } else {
                            writeMethod.invoke(target, new Object[]{null});
                        }

                    } catch (Throwable ex) {
                        throw new FatalBeanException(
                                "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                    }
                }

            }
            else if (IdEntity.class.isAssignableFrom(parameterType)) {
                // 字符串转实体
                PropertyDescriptor readDescirptor = BeanUtils.getPropertyDescriptor(source.getClass(), megerFieldName(targetPd.getName(), "id", false));
                if (readDescirptor != null && readDescirptor.getReadMethod() != null
                        && String.class.isAssignableFrom(readDescirptor.getReadMethod().getReturnType())) {
                    Method readMethod = readDescirptor.getReadMethod();
                    if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                        readMethod.setAccessible(true);
                    }
                    if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                        writeMethod.setAccessible(true);
                    }
                    try {
                        String value = String.class.cast(readMethod.invoke(source));
                        if (StringUtils.hasText(value)) {
                            ApplicationContext applicationContext = ApplicationHolder.getApplicationContext();
                            String[] beanNamesForType = applicationContext.getBeanNamesForType(
                                    ResolvableType.forClassWithGenerics(BaseService.class, parameterType)
                            );

                            if(beanNamesForType.length>0){
                                BaseService bean = applicationContext.getBean(beanNamesForType[0], BaseService.class);
                                writeMethod.invoke(target, bean.findRequiredById(value));
                            }else{
                                writeMethod.invoke(target, new Object[]{null});
                            }

                        } else {
                            writeMethod.invoke(target, new Object[]{null});
                        }

                    } catch (Throwable ex) {
                        throw new FatalBeanException(
                                "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                    }
                }

            }
            else  {
                // 写的属性名
                String name = targetPd.getName();
                // 找到可能的嵌套属性集合比如 aaBbCcDd 可能是aaBbCc属性的dd属性/ aaBb属性的ccDd属性......
                List<MegerFieldName> megerFieldSourceName = findMegerFieldSourceName(name);
                List<MegerFieldName> avalidMegerFieldSourceName = new ArrayList<>();

                for (MegerFieldName megerFieldName : megerFieldSourceName) {
                    // 合并字段名称
                    String megerName = megerFieldName.getMegerName();
                    // 属性字段名称
                    String sourceName = megerFieldName.getSourceName();
                    // 属性描述
                    sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), sourceName);
                    // 此属性无读属性
                    if (Objects.isNull(sourcePd) || Objects.isNull(sourcePd.getReadMethod())) {
                        continue;
                    }
                    // 此属性无读属性
                    Method readMethod = sourcePd.getReadMethod();
                    // 判断此读方法的返回
                    Class<?> returnType = readMethod.getReturnType();
                    if (Collection.class.isAssignableFrom(returnType)) {
                        returnType = ResolvableType.forMethodReturnType(readMethod).getGeneric(0).resolve();
                    }
                    PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(returnType, megerName);
                    if (Objects.isNull(propertyDescriptor) || Objects.isNull(propertyDescriptor.getReadMethod())) {
                        continue;
                    }
                    avalidMegerFieldSourceName.add(megerFieldName);
                }
                if(avalidMegerFieldSourceName.size()>1) {
                    throw new IllegalArgumentException("多个适用的合并属性， "+name+"_"+Arrays.toString(avalidMegerFieldSourceName.toArray()));
                }
                // 复制属性没有匹配的
                if(avalidMegerFieldSourceName.size() == 0) {
                    continue;
                }
                String megerName = avalidMegerFieldSourceName.get(0).getMegerName();
                // 属性字段名称
                String sourceName = avalidMegerFieldSourceName.get(0).getSourceName();
                sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), sourceName);
                Method readMethod = sourcePd.getReadMethod();
                if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                    readMethod.setAccessible(true);
                }
                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                    writeMethod.setAccessible(true);
                }
                Class<?> returnType = readMethod.getReturnType();
                // 返回类型为集合
                if (Collection.class.isAssignableFrom(returnType)) {
                    try {
                        // 获取集合值
                        Collection<?> values = (Collection<?>) readMethod.invoke(source);
                        // 此集合需要合并的值定义
                        List<String> stringList = new ArrayList<>();
                        for (Object value : values) {
                            Method readMethod1 = BeanUtils.getPropertyDescriptor(value.getClass(), megerName).getReadMethod();
                            if (!Modifier.isPublic(readMethod1.getDeclaringClass().getModifiers())) {
                                readMethod1.setAccessible(true);
                            }
                            Object o = readMethod1.invoke(value);
                            if (o != null) {
                                stringList.add(String.valueOf(o));
                            }

                        }
                        if (stringList.size() > 0) {
                            writeMethod.invoke(target, StringUtils.collectionToCommaDelimitedString(stringList));
                            continue;
                        }

                    } catch (Throwable ex) {
                        continue;
                    }
                }
                else {
                    try {
                        Object o = readMethod.invoke(source);
                        if (o != null) {
                            Method readMethod1 = BeanUtils.getPropertyDescriptor(o.getClass(), megerName).getReadMethod();
                            if (!Modifier.isPublic(readMethod1.getDeclaringClass().getModifiers())) {
                                readMethod1.setAccessible(true);
                            }
                            Object v = readMethod1.invoke(o);

                            if (v != null) {
                                Object convert = ApplicationHolder.getApplicationContext().getBean(ConversionService.class).convert(v, parameterType);
                                writeMethod.invoke(target, convert);
                                continue;
                            }

                        }
                    }catch (Throwable ex) {
                        continue;
                    }
                }
            }
        }

    }
}
